using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Dss.Core;
using Microsoft.Dss.Core.Attributes;
using System.ComponentModel;

namespace VEXRobotics
{
    /// <summary>
    /// Type of sensor attached to the IO Port
    /// </summary>
    [DataContract]
    [Description("Interrupt port used.")]
    public enum InterruptPort
    {
        /// <summary>
        /// No port
        /// </summary>
        [Description("No port used")]
        InterruptPort_None = 0,

        /// <summary>
        /// Interrupt Port 1
        /// </summary>
        [Description("Interrupt Port 1")]
        InterruptPort_1 = 1,

        /// <summary>
        /// Interrupt Port 2
        /// </summary>
        [Description("Interrupt Port 2")]
        InterruptPort_2 = 2,

        /// <summary>
        /// Interrupt Port 3
        /// </summary>
        [Description("Interrupt Port 3")]
        InterruptPort_3 = 3,

        /// <summary>
        /// Interrupt Port 4
        /// </summary>
        [Description("Interrupt Port 4")]
        InterruptPort_4 = 4,

        /// <summary>
        /// Interrupt Port 5
        /// </summary>
        [Description("Interrupt Port 5")]
        InterruptPort_5 = 5,

        /// <summary>
        /// Interrupt Port 6
        /// </summary>
        [Description("Interrupt Port 6")]
        InterruptPort_6 = 6
    }

    /// <summary>
    /// A single interrupt sensor
    /// </summary>
    [DataContract]
    public class InterruptSensorData
    {
        private SensorDefinition.SensorType _type;
        private SensorPort _port;
        private UInt32 _value;

        /// <summary>
        /// Type of the sensor
        /// </summary>
        [DataMember]
        public SensorDefinition.SensorType Type
        {
            get { return _type; }
            set { _type = value; }
        }

        /// <summary>
        /// Current value
        /// </summary>
        [DataMember]
        public UInt32 Value
        {
            get { return _value; }
            set { _value = value; }
        }

        /// <summary>
        /// IO port used (if sonar)
        /// </summary>
        [DataMember]
        public SensorPort Port
        {
            get { return _port; }
            set { _port = value; }
        }

        /// <summary>
        /// Default constructor
        /// </summary>
        public InterruptSensorData()
        {
            _type = SensorDefinition.SensorType.None;
            _value = 0;
            _port = SensorPort.SensorPort_None;
        }

        /// <summary>
        /// Default constructor for a type
        /// </summary>
        /// <param name="type">Type of sensor on the interrupt</param>
        public InterruptSensorData(SensorDefinition.SensorType type)
        {
            _type = type;
            _value = 0;
            _port = SensorPort.SensorPort_None;
        }
    }

    /// <summary>
    /// A bank of interrupt sensors
    /// </summary>
    [DataContract]
    public class InterruptSensorBank : IDssSerializable, ICloneable
    {
        private InterruptSensorData[] _interruptSensors = new InterruptSensorData[6];
        private uint _changes;

        /// <summary>
        /// Default constructor
        /// </summary>
        public InterruptSensorBank()
        {
            _interruptSensors[0] = new InterruptSensorData();
            _interruptSensors[1] = new InterruptSensorData();
            _interruptSensors[2] = new InterruptSensorData();
            _interruptSensors[3] = new InterruptSensorData();
            _interruptSensors[4] = new InterruptSensorData();
            _interruptSensors[5] = new InterruptSensorData();
        }

        #region Sensor port

        /// <summary>
        /// Encoder sensor array
        /// </summary>
        [DataMember()]
        public InterruptSensorData[] Sensors
        {
            get { return this._interruptSensors; }
            set { this._interruptSensors = value; }
        }

        #endregion

        #region Interrupt sensor Values

        /// <summary>
        /// Set the value for an IRQ
        /// </summary>
        /// <param name="irq">Interrupt number (0 based)</param>
        /// <param name="value">Value associated with the port</param>
        public void SetValue(int irq, UInt32 value)
        {
            _interruptSensors[irq].Value = value;
        }

        /// <summary>
        /// Encoder sensor current value
        /// </summary>
        [DataMember()]
        public UInt32[] Value
        {
            get
            {
                UInt32[] value = new UInt32[6];
                int i;
                for (i = 0; i < 6; ++i)
                {
                    value[i] = _interruptSensors[i].Value;
                }
                return value;
            }
            set
            {
                int i;
                for (i = 0; i < 6; ++i)
                {
                    _interruptSensors[i].Value = value[i];
                }
            }
        }

        #endregion

        #region Change Flags

        /// <summary>
        /// Set that a port changed values
        /// </summary>
        /// <param name="port">Port that changed (1 based)</param>
        public void SetChanged(int port)
        {
            if (port >= 1 && port <= 6)
                _changes |= (uint)(1 << (port - 1));
        }

        /// <summary>
        /// One bit set for every sensor that changed since last time slice
        /// </summary>
        [DataMember()]
        public uint Changes
        {
            get { return this._changes; }
            set { this._changes = value; }
        }

        /// <summary>
        /// Simple enum to aid coding
        /// </summary>
        [Flags]
        public enum Mask : uint
        {
            Encoder1 = 0x00000001,
            Encoder2 = 0x00000002,
            Encoder3 = 0x00000004,
            Encoder4 = 0x00000008,
            Encoder5 = 0x00000010,
            Encoder6 = 0x00000020,
        }

        #endregion


        #region ICloneable Members

        /// <summary>
        /// Define how to clone ourselves
        /// </summary>
        /// <returns></returns>
        public object Clone()
        {
            InterruptSensorBank config = new InterruptSensorBank();
            config.Changes = this.Changes;
            for(int i=0; i<Sensors.Length; ++i)
            {
                config.Sensors[i].Port = this.Sensors[i].Port;
                config.Sensors[i].Type = this.Sensors[i].Type;
                config.Sensors[i].Value = this.Sensors[i].Value;
            }
            config.Value = this.Value;
            return config;
        }

        #endregion


        #region IDssSerializable

        /// <summary>
        /// Copy To state
        /// </summary>
        public virtual void CopyTo(IDssSerializable target)
        {
            InterruptSensorBank typedTarget = target as InterruptSensorBank;

            if (typedTarget == null)
                throw new ArgumentException("CopyTo({0}) requires type {0}", this.GetType().FullName);

            for (int i = 0; i < this.Sensors.Length; ++i)
            {
                typedTarget.Sensors[i].Port = this.Sensors[i].Port;
                typedTarget.Sensors[i].Type = this.Sensors[i].Type;
                typedTarget.Sensors[i].Value = this.Sensors[i].Value;
            }
        }

        /// <summary>
        /// Serialize
        /// </summary>
        public virtual void Serialize(System.IO.BinaryWriter writer)
        {
            for (int i = 0; i < this.Sensors.Length; ++i)
            {
                writer.Write((int) this.Sensors[i].Port);
                writer.Write((int) this.Sensors[i].Type);
                writer.Write(this.Sensors[i].Value);
            }
        }

        /// <summary>
        /// Deserialize
        /// </summary>
        public virtual object Deserialize(System.IO.BinaryReader reader)
        {
            for (int i = 0; i < this.Sensors.Length; ++i)
            {
                this.Sensors[i].Port = (SensorPort)reader.ReadInt32();
                this.Sensors[i].Type = (SensorDefinition.SensorType)reader.ReadInt32();
                this.Sensors[i].Value = reader.ReadUInt32();
            }

            return this;
        }
        #endregion
    }
}
